//
//  MobileDeviceController.m
//  eBookr
//
//  Created by Zachary Brewster-Geisz on 9/22/07.
//  Copyright 2007 __MyCompanyName__. All rights reserved.
//

#import "MobileDeviceController.h"


@implementation MobileDeviceController
- (MobileDeviceController *)init
{
	self = [super init];
	iphoneCurrentStatus = iphoneDisconnected;
	theDevStruct.notif = malloc(sizeof(struct am_device_notification));
	mach_error_t err = AMDeviceNotificationSubscribe(&callbackFunction, 0, 0, 0, &theDevStruct.notif);
	if (err)
	{
		NSLog(@"AMDeviceNotificationSubscribe returned error: %d", err);
		iphoneCurrentStatus = iphoneError;
		[[NSNotificationCenter defaultCenter] postNotificationName:IPHONE_ERROR object:self];
	}
	else
		NSLog(@"AMDeviceNotificationSubscribe successful.");
	return self;
}

void callbackFunction(struct am_device_notification_callback_info *info)
{
	NSLog(@"callbackFunction!");
	mach_error_t err;
	switch (info->msg)
	{
		case ADNCI_MSG_CONNECTED:
			if (iphoneCurrentStatus == iphoneDisconnected)
			{
				theDevStruct.theDevice = info->dev;
				err = AMDeviceConnect(theDevStruct.theDevice);
				if (MDERR_OK != err)
				{
					NSLog(@"Error connecting to iPhone.");
					iphoneCurrentStatus = iphoneError;
				}
				err = AMDeviceValidatePairing(theDevStruct.theDevice);
				if (MDERR_OK != err)
				{
					NSLog(@"Error pairing with iPhone.");
					iphoneCurrentStatus = iphoneError;
				}
				err =	AMDeviceStartSession(theDevStruct.theDevice);
				if (MDERR_OK != err)
				{
					NSLog(@"Error starting Lockdown session.");
					iphoneCurrentStatus = iphoneError;
				}			
				err = AMDeviceStartService(theDevStruct.theDevice, AMSVC_AFC, &theDevStruct.theHandle, NULL);
				if (MDERR_OK != err)
				{
					NSLog(@"Error starting AFC service.");
					iphoneCurrentStatus = iphoneError;
				}
				err = AFCConnectionOpen(theDevStruct.theHandle, 0, &theDevStruct.theConnection);
				if (MDERR_OK != err)
				{
					NSLog(@"Error opening AFC connection.");
					iphoneCurrentStatus = iphoneError;
				}
				
				//[self populateEBookFolderTree];
				if (iphoneCurrentStatus == iphoneError)
				{
					NSDictionary *temp = [NSDictionary dictionaryWithObject:@"Error connecting to iPhone."
																	 forKey:ERRORMESSAGE_KEY];
					[[NSNotificationCenter defaultCenter] postNotificationName:IPHONE_ERROR
																		object:nil
																	  userInfo:temp];
				}
				else
				{
					iphoneCurrentStatus = iphoneConnected;
					NSLog(@"Successfully opened AFC connection.");
					[[NSNotificationCenter defaultCenter] postNotificationName:IPHONE_CONNECTED
																		object:nil];
				}
			}
			break;
		case ADNCI_MSG_DISCONNECTED:
			iphoneCurrentStatus = iphoneDisconnected;
			[[NSNotificationCenter defaultCenter] postNotificationName:IPHONE_DISCONNECTED
																object:nil];
			break;
	}
}

- (NSArray *)saveBooksFromPhoneToLocalPath:(NSString *)path
{
	NSMutableArray *working = [[NSMutableArray alloc] init];
	NSArray *ret;
	char *directoryEntry;
	directoryEntry = malloc(1024);
	directoryEntry[0] = 0;
	NSString *baseBooksPath = @"/EBooks/";
	struct afc_directory *dir, *subdir;
	dir = malloc(sizeof(struct afc_directory));
	subdir = malloc(sizeof(struct afc_directory));
	afc_error_t result = AFCDirectoryOpen(theDevStruct.theConnection, [baseBooksPath UTF8String], &dir);
	if (result)
	{
		NSLog(@"Could not open base directory.");
		return nil;
	}
	result = AFCDirectoryRead(theDevStruct.theConnection, dir, &directoryEntry);
	while ((directoryEntry != NULL) && !result)
	{
		NSString *dirPath = [NSString stringWithCString:directoryEntry];
		if (!([dirPath isEqualToString:@"."] || [dirPath isEqualToString:@".."]))
		{
			NSString *fullLocalPath = [path stringByAppendingPathComponent:dirPath];
			NSString *fullPath = [baseBooksPath stringByAppendingPathComponent:dirPath];
			int result2 = AFCDirectoryOpen(theDevStruct.theConnection, [fullPath UTF8String], &subdir);
			if (result2)
			{
				// If we got here, it's probably not a directory.
				NSLog(@"Not a directory: %@", fullPath);
				[self copyFileAtRemotePath:fullPath toLocalBase:path];
			}
			else
			{
				//fullLocalPath = [fullLocalPath stringByAppendingPathComponent:@"/"];
				[[NSFileManager defaultManager] createDirectoryAtPath:fullLocalPath attributes:nil];
				// Probably a directory.
				[self mirrorDirectoryAtRemotePath:fullPath toLocalDir:fullLocalPath];
			}
			[working addObject:fullLocalPath];
		}
		result = AFCDirectoryRead(theDevStruct.theConnection, dir, &directoryEntry);
	}
	free(dir);
	free(subdir);
	free(directoryEntry);
	ret = [NSArray arrayWithArray:working];
	[working release];
	return ret;
}

- (int)mirrorDirectoryAtRemotePath:(NSString *)remotePath toLocalDir:(NSString *)localDir
{
	char *directoryEntry;
	directoryEntry = malloc(1024);
	directoryEntry[0] = 0;
	struct afc_directory *dir, *subdir;
	dir = malloc(sizeof(struct afc_directory));
	subdir = malloc(sizeof(struct afc_directory));
	afc_error_t result = AFCDirectoryOpen(theDevStruct.theConnection, [remotePath UTF8String], &dir);
	if (result)
	{
		NSLog(@"Could not open base directory.");
		return 1;
	}
	result = AFCDirectoryRead(theDevStruct.theConnection, dir, &directoryEntry);
	while ((directoryEntry != NULL) && !result)
	{
		NSString *dirPath = [NSString stringWithCString:directoryEntry];
		if (!([dirPath isEqualToString:@"."] || [dirPath isEqualToString:@".."]))
		{
			NSString *fullLocalPath = [localDir stringByAppendingPathComponent:dirPath];
			NSString *fullPath = [remotePath stringByAppendingPathComponent:dirPath];
			int result2 = AFCDirectoryOpen(theDevStruct.theConnection, [fullPath UTF8String], &subdir);
			if (result2)
			{
				// If we got here, it's probably not a directory.
				NSLog(@"Not a directory: %@", fullPath);
				[self copyFileAtRemotePath:fullPath toLocalBase:localDir];
			}
			else
			{
				//fullLocalPath = [fullLocalPath stringByAppendingPathComponent:@"/"];
				[[NSFileManager defaultManager] createDirectoryAtPath:fullLocalPath attributes:nil];
				// Probably a directory.
				[self mirrorDirectoryAtRemotePath:fullPath toLocalDir:fullLocalPath];
			}
		}
		result = AFCDirectoryRead(theDevStruct.theConnection, dir, &directoryEntry);
	}
	free(dir);
	free(subdir);
	free(directoryEntry);
	return result;
}
- (int)copyFileAtRemotePath:(NSString *)remotePath toLocalBase:(NSString *)localBase
{
	afc_error_t result;
	afc_file_ref ref;
	struct afc_dictionary *info;
	unsigned int size = 0;
	unsigned char *buf;
	char *key, *val;
	NSData *fileData;
	NSMutableDictionary *dict = [NSMutableDictionary dictionary];
	NSString *localPath = [localBase stringByAppendingPathComponent:[remotePath lastPathComponent]];
	NSString *message;
	//info = malloc(sizeof(struct afc_dictionary));
	
	//We need to find the size of the file.
	result = AFCFileInfoOpen(theDevStruct.theConnection, [remotePath UTF8String], &info);
	if (result)
	{
		NSLog(@"Error: could not open file info at path %@", remotePath);
		return 1;
	}
	while (1) 
	{
		AFCKeyValueRead(info, &key, &val);
		if (!key || !val)
			break;
		
		if (!strcmp(key, "st_size")) 
		{
			sscanf(val, "%u", &size);
			break;
		}
	}
	if (0 == size)
	{
		NSLog(@"Error: zero size for file at path %@", remotePath);
		return 1;
	}
	AFCKeyValueClose(info);
	//free(info);
	
	result = AFCFileRefOpen(theDevStruct.theConnection, [remotePath UTF8String], 1, &ref);
	if (result)
	{
		NSLog(@"Error: could not open %@ for reading", remotePath);
		return 1;
	}
	buf = (unsigned char *)malloc(size);
	if (!buf)
	{
		NSLog(@"Error: could not allocate save buffer.");
		return 1;
	}
	result = AFCFileRefRead(theDevStruct.theConnection, ref, buf, &size);
	if (result)
	{
		NSLog(@"Failed to read %@", remotePath);
		return 1;
	}
	result = AFCFileRefClose(theDevStruct.theConnection, ref);
	
	fileData = [[NSData alloc] initWithBytesNoCopy:buf length:size];
	if (nil == fileData)
	{
		NSLog(@"Could not create local data.");
		return 1;
	}
	message = [NSString stringWithFormat:@"Copying %@...", [localPath lastPathComponent]];
	[dict setObject:message forKey:ERRORMESSAGE_KEY];
	[[NSNotificationCenter defaultCenter] postNotificationName:IPHONE_MESSAGE object:self userInfo:dict];
	BOOL success = [fileData writeToFile:localPath atomically:YES];
	if (!success)
	{
		NSLog(@"Could not write local file %@", localPath);
	}
	[fileData release];
	return 0;
	//free(buf);  // Not needed since NSData handles freeing it
}

- (NSArray *)booksInRootFolder
// Returns a simple list of the files in the EBooks folder.
{
	NSMutableArray *working = [[NSMutableArray alloc] init];
	NSArray *ret;
	char *directoryEntry;
	directoryEntry = malloc(1024);
	directoryEntry[0] = 0;
	NSString *baseBooksPath = @"/EBooks/";
	struct afc_directory *dir;
	dir = malloc(sizeof(struct afc_directory));
	afc_error_t result = AFCDirectoryOpen(theDevStruct.theConnection, [baseBooksPath UTF8String], &dir);
	if (result)
	{
		NSLog(@"Could not open base directory.");
		return [working autorelease];
	}
	result = AFCDirectoryRead(theDevStruct.theConnection, dir, &directoryEntry);
	while ((directoryEntry != NULL) && !result)
	{
		NSString *dirPath = [NSString stringWithCString:directoryEntry];
		if (!([dirPath isEqualToString:@"."] || [dirPath isEqualToString:@".."]))
		{
			[working addObject:dirPath];
		}
		result = AFCDirectoryRead(theDevStruct.theConnection, dir, &directoryEntry);
	}
	free(directoryEntry);
	free(dir);
	ret = [NSArray arrayWithArray:working];
	[working release];
	return ret;
}

- (int)copyTheseFiles:(NSArray *)files
{
	NSEnumerator *pathEnum = [files objectEnumerator];
	
	NSString *currentPath;
	int ret = 0;
	while (currentPath = [pathEnum nextObject])
	{
		BOOL isDir = NO;
		BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:currentPath
															   isDirectory:&isDir];
		int result = AFCDirectoryCreate(theDevStruct.theConnection, "/EBooks/");
		if (result)
		{
			NSLog(@"Could not create EBooks folder.");
			NSDictionary *dict = [NSDictionary dictionaryWithObject:@"Could not create EBooks folder."
															 forKey:ERRORMESSAGE_KEY];
			[[NSNotificationCenter defaultCenter] postNotificationName:IPHONE_ERROR
																object:self
															  userInfo:dict];
			return 0;
		}
		if (fileExists)
		{
			if (isDir)
			{
				ret += [self drillIntoSubdirectory:currentPath];
			}
			else
			{
				ret += [self copyThisFile:currentPath
								  baseDir:[currentPath stringByDeletingLastPathComponent]];
			}
		}
	}
	return ret;
}

- (int)drillIntoSubdirectory:(NSString *)currentPath
{
	int ret = 0;
	NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:currentPath];
	NSString *relPath;
	//NSString *basePath = [currentPath stringByDeletingLastPathComponent];
	NSString *remoteEBookPath = [[NSString stringWithString:@"/EBooks/"]
				stringByAppendingPathComponent:[currentPath lastPathComponent]];
	NSLog(@"mkdir %@", remoteEBookPath);
	int result = AFCDirectoryCreate(theDevStruct.theConnection, [remoteEBookPath UTF8String]);
	if (result)
	{
		NSLog(@"Could not create folder.");
		NSDictionary *dict = [NSDictionary dictionaryWithObject:@"Could not create folder."
														 forKey:ERRORMESSAGE_KEY];
		[[NSNotificationCenter defaultCenter] postNotificationName:IPHONE_ERROR
															object:self
														  userInfo:dict];
	}
	while (relPath = [dirEnum nextObject])
	{
		BOOL isDir = NO;
		BOOL fileExists = 
			[[NSFileManager defaultManager] fileExistsAtPath:[currentPath stringByAppendingPathComponent:relPath]
												 isDirectory:&isDir];
		if (fileExists)
		{
			if (isDir)
			{
				NSLog(@"mkdir %@", [remoteEBookPath stringByAppendingPathComponent:relPath]);
				int result = AFCDirectoryCreate(theDevStruct.theConnection, 
												[[remoteEBookPath stringByAppendingPathComponent:relPath]
													UTF8String]);
				if (result)
				{
					NSLog(@"Could not create folder.");
					NSDictionary *dict = [NSDictionary dictionaryWithObject:@"Could not create folder."
																	 forKey:ERRORMESSAGE_KEY];
					[[NSNotificationCenter defaultCenter] postNotificationName:IPHONE_ERROR
																		object:self
																	  userInfo:dict];
				}
			}
			else
			{
				int junk = [self copyThisFile:[currentPath stringByAppendingPathComponent:relPath]
									  baseDir:[currentPath stringByDeletingLastPathComponent]];
			}
		}
	}
	return 1;	
}

- (int)copyThisFile:(NSString *)filePath baseDir:(NSString *)baseDir
{
	afc_file_ref ref;
	NSString *baseEBookPath = @"/EBooks/";
	NSString *localFilePath = filePath;
	NSString *remoteFilePath = [baseEBookPath stringByAppendingPathComponent:
		[filePath substringFromIndex:[baseDir length]]];
	int result;
	NSDictionary *dict;
	
	NSData *theData = [NSData dataWithContentsOfFile:localFilePath];
	if (nil == theData)
	{
		NSLog(@"Could not open local file.");
		dict = [NSDictionary dictionaryWithObject:@"Could not open local file."
										   forKey:ERRORMESSAGE_KEY];
		[[NSNotificationCenter defaultCenter] postNotificationName:IPHONE_ERROR
															object:self
														  userInfo:dict];
		return 0;
	}
	result = AFCFileRefOpen(theDevStruct.theConnection, [remoteFilePath UTF8String], 3, &ref);
	if (result)
	{
		NSLog(@"Could not create file for %@.", remoteFilePath);
		dict = [NSDictionary dictionaryWithObject:@"Could not create file."
														 forKey:ERRORMESSAGE_KEY];
		[[NSNotificationCenter defaultCenter] postNotificationName:IPHONE_ERROR
															object:self
														  userInfo:dict];
		return 0;
	}
	NSString *message = [NSString stringWithFormat:@"Copying %@...", [remoteFilePath lastPathComponent]];
	dict = [NSDictionary dictionaryWithObject:message
									   forKey:ERRORMESSAGE_KEY];
	[[NSNotificationCenter defaultCenter] postNotificationName:IPHONE_MESSAGE
														object:self
													  userInfo:dict];	
	result = AFCFileRefWrite(theDevStruct.theConnection, ref, [theData bytes], [theData length]);
	if (result)
	{
		NSLog(@"Could not write to file.");
		dict = [NSDictionary dictionaryWithObject:@"Could not write to file."
														 forKey:ERRORMESSAGE_KEY];
		[[NSNotificationCenter defaultCenter] postNotificationName:IPHONE_ERROR
															object:self
														  userInfo:dict];
		return 0;
	}
	result = AFCFileRefClose(theDevStruct.theConnection, ref);
	if (result)
	{
		NSLog(@"Could not close file.");
		dict = [NSDictionary dictionaryWithObject:@"Could not close file."
														 forKey:ERRORMESSAGE_KEY];
		[[NSNotificationCenter defaultCenter] postNotificationName:IPHONE_ERROR
															object:self
														  userInfo:dict];
		return 0;
	}

	//NSLog(@"copyThisFile %@ (%@)", remoteFilePath, [baseDir stringByAppendingPathComponent:filePath]);
	return 1;
}

//void put_file(struct shell_state *sh, char *local_path, char *remote_path)
//{
//	FILE *f;
//	struct stat sb;
//	unsigned char *buf;
//	unsigned int size;
//	afc_file_ref ref;
//	std::string remotePathStr;
//	afc_error_t ret;
//	
//	ifVerbose cout << "putfile: Opening local file '" << local_path << "'" << endl;
//	
//	f = fopen(local_path, "rb");
//	if (!f) {
//		ifNotQuiet cout << "putfile: Failed to open local file '" << local_path << "'" << endl;
//		return;
//	}
//	
//	D("local file open.");
//	
//	// If remote_path isn't set, use cp semantics
//	if(remote_path == NULL)
//	{
//		// putfile foo/bar
//		// should create a file named bar in the current dir
//		string local = local_path;
//		if(local[local.length() - 1] == '/')
//		{
//			ifNotQuiet cout << "putfile: Writing directories is currently unsupported" << endl;
//			return;
//		}
//		
//		string::size_type lastSlash = local.find_last_of("/");
//		if(lastSlash == string::npos)
//		{
//			// simple filename in local_path
//			remotePathStr = local_path;
//		} else {
//			remotePathStr = local.substr(lastSlash + 1);
//		}
//		
//	} else {
//		remotePathStr = remote_path;
//	}
//	
//	D("remote path: "<<remote_path);
//	remote_path = (char *) remotePathStr.c_str();
//	
//	size = fstat(fileno(f), &sb);
//	D("local fileno: "<< fileno(f));
//	
//	
//	// AFCFileRefWrite: Writing to remote file
//	// putfile: Failed to write to remote file: 11
//	// is usually caused by trying to write a file with size 0
//	if( sb.st_size == 0 )
//	{
//		ifNotQuiet cout << "putfile: Cannot write file with size " << sb.st_size << endl;
//		return;
//	}
//	
//	buf = (unsigned char *)malloc(sb.st_size);
//	
//	if(!buf)
//	{
//		ifNotQuiet cout << "putfile: Could not allocate buffer.  Aborting." << endl;
//		return;
//	}
//	
//	D("reading file into buffer.");
//	fread(buf, sb.st_size, 1, f);
//	D("closing file.");
//	fclose(f);
//	
//	ifVerbose cout << "AFCFileRefOpen: opening remote file '" << remote_path << "'" << endl;
//	ret = AFCFileRefOpen(sh->conn, remote_path, 3, &ref);
//	
//	if (ret) {
//		ifNotQuiet cout << "putfile: Failed to open remote file: " << ret << endl;
//		return;
//	}
//	
//	ifVerbose cout << "AFCFileRefWrite: Writing to remote file" << endl;
//	ret = AFCFileRefWrite(sh->conn, ref, buf, sb.st_size);
//	
//	if (ret) {
//		ifNotQuiet cout << "putfile: Failed to write to remote file: " << ret << endl;
//		return;
//	}
//	
//	ifVerbose cout << "AFCFileRefClose: Closing remote file. " << endl;
//	ret = AFCFileRefClose(sh->conn, ref);
//	
//	if (ret) {
//		ifNotQuiet cout << "putfile: Failed to close remote file: " << ret << endl;
//		return;
//	}
//	
//	D("freeing file buffer.");
//	free(buf);
//	
//	ifVerbose cout << "putfile: Transfer successful" << endl;
//}

- (void)dealloc
{
	free(theDevStruct.notif);
	[super dealloc];
}
@end
